//	--------------------------------------------------------------------
//	Copyright(C) 2006,2007 Zhao Yukun. All rights reserved.
//	This file is a part of the Xeres system.
//	Xeres 2007
//	Contact : xeres.engine@gmail.com
//	$(reservedInfo)
//	$(reservedDeclaration)
//	--------------------------------------------------------------------
///	@file	<XGfxSlide.cpp>
///	@path	~/src/appear/xgfx/
///	@date	2008/10/14
///	@brief	.

#include "config/config.h"

#include "object/CVar.h"

#include "graphics/IRenderer.h"
#include "graphics/ITexture.h"

#include "appear/xgfx/XGfxSlide.h"

namespace xeres {

	IMPL_IOBJECT_CLASS_DYNAMIC( XGfxSlide );

	// XGfxSlide
	XGfxSlide::XGfxSlide( void )
		: m_paramElapse( 0.3 )
		, m_paramSlideType( SLIDE_OUT )
		, m_tickElapsed( 0 )
	{
	}

	// XGfxSlide
	XGfxSlide::~XGfxSlide( void )
	{
		m_texture = NULL;
		m_technique = NULL;
		m_effect = NULL;
	}

	// SetComp
	void XGfxSlide::SetComp( RefWeak<IGuiComp> comp )
	{
		m_comp = comp;
		m_tickElapsed = 0;
	}

	// GetComp
	RefWeak<IGuiComp> XGfxSlide::GetComp( void )
	{
		return m_comp;
	}

	// SetParam
	bool XGfxSlide::SetParam( const WString& name , Ref<IVar> val )
	{
		if( name == _S("elapse") )
		{
			return val->GetFloat( m_paramElapse );
		}
		else if( name == _S("type") )
		{
			return val->GetInt( m_paramSlideType );
		}
		else if( name == _S("pos") )
		{
			return val->GetPair( m_paramSlidePos );
		}
		else if( name == _S("scale") )
		{
			return val->GetPair( m_paramSlideScale );
		}
// 		else if( name == _S("tick") )
// 		{
// 			float tick = 0;
// 			if( ! val->GetFloat( tick ) ) return false;
// 			clamp_ref<float>( tick , 0 , 1 );
// 			m_tickElapsed = tick * m_paramElapse;
// 			return true;
// 		}
		return false;
	}

	// GetParam
	RefPass<IVar> XGfxSlide::GetParam( const WString& name )
	{
		if( name == _S("elapse") )
		{
			return new CVar( m_paramElapse );
		}
		else if( name == _S("type") )
		{
			return new CVar( (int)m_paramSlideType );
		}
		else if( name == _S("pos") )
		{
			return new CVar( m_paramSlidePos );
		}
		else if( name == _S("scale") )
		{
			return new CVar( m_paramSlideScale );
		}
		else if( name == _S("tick") )
		{
			return new CVar( m_tickElapsed / m_paramElapse );
		}
		return NULL;
	}

	// GetParamList
	StringSeq XGfxSlide::GetParamList( void )
	{
		static StringSeq s_paramSeq;
		if( s_paramSeq.empty() )
		{
			s_paramSeq.push_back( _S("float:tick") );
			s_paramSeq.push_back( _S("float:elapse") );
			s_paramSeq.push_back( _S("int:type") );
			s_paramSeq.push_back( _S("pair:pos") );
			s_paramSeq.push_back( _S("pair:scale") );
		}
		return s_paramSeq;
	}

	// Reset
	void XGfxSlide::Reset( void )
	{
		m_tickElapsed = 0;
	}

	// Tick
	bool XGfxSlide::Tick( float dTime )
	{
		if( m_tickElapsed >= m_paramElapse )
		{
			m_tickElapsed = 0;
			return false;
		}
		m_tickElapsed += dTime;

		// make alpha
		if( m_tickElapsed > m_paramElapse )
			m_tickElapsed = m_paramElapse;
		return true;
	}

	namespace
	{
		struct _GfxVertex
		{
			FLOAT x, y, z, rhw;
			float u , v;
		};

static const char * shader = "\n\
texture g_FrameTexture;\n\
sampler FrameSampler = sampler_state {\n\
    Texture = <g_FrameTexture>;\n\
    MinFilter = LINEAR;\n\
    MagFilter = LINEAR;\n\
	AddressU = CLAMP;\n\
	AddressV = CLAMP;\n\
};\n\
float4 EffectProcess( float2 t : TEXCOORD0 ) : COLOR0 {\n\
	float4 color = tex2D( FrameSampler , t );\n\
	if( t.x > 1 || t.x < 0 || t.y > 1 || t.y < 0 )\n\
		discard;\n\
	return color;\n\
}\n\
technique RenderFrame {\n\
    pass P0 {\n\
		AlphaBlendEnable = TRUE;\n\
		SeparateAlphaBlendEnable = TRUE;\n\
        VertexShader = NULL;\n\
        PixelShader  = compile ps_2_0 EffectProcess();\n\
    }\n\
}\n\
";
	}

	// Render
	void XGfxSlide::Render( RenderContext& rc , RefWeak<ITexture> texture )
	{
		if( m_effect.IsNull() )
		{
			m_effect = rc.GetRenderer()->CreateEffect( shader , strlen(shader) , NULL );
			m_technique = m_effect->GetTechnique( "RenderFrame" );
			m_texture = m_effect->GetVariableByName( "g_FrameTexture" );
		}

		Pair<int> start = m_comp->GetScreenPosition();
		Pair<int> size = m_comp->GetSize();

		float ratio = m_tickElapsed / m_paramElapse;
		bool halfway = ratio >= 0.5;
		ratio *= 2;
		if( ratio > 1 )
			ratio -= 1;

		if( m_paramSlideType == SLIDE_OUT )
		{
			ratio = ( 1 - ratio );
		}

		m_texture->SetByTexture( texture );
		{
			float ix = m_paramSlidePos.x;
			float iy = m_paramSlidePos.y;
			float wx = ix + m_paramSlideScale.x;
			float wy = iy + m_paramSlideScale.y;

			Rect<int> placement( start , start + size );

			float px = float(placement.left()) , py = float(placement.top()) ,
				sx = float(placement.right()) , sy = float(placement.bottom()) ;

			//	ts te
			//	bs be
			//
			float ts = px , bs = px , te = sx , be = sx;

			if( m_paramSlideType == SLIDE_OUT )
			{
				if( !halfway )
				{
					bs = mix<float>( px , ix , 1 - ratio );
					be = mix<float>( sx , wx , 1 - ratio );
					sy = mix<float>( sy , wy , 1 - ratio );
				}
				else
				{
					ts = mix<float>( px , ix , 1 - ratio );
					te = mix<float>( sx , wx , 1 - ratio );
					py = mix<float>( py , iy , 1 - ratio );

					sy = wy;
					bs = ix;
					be = wx;
				}
			}
			else
			{
				if( !halfway )
				{
					ts = mix<float>( px , ix , 1 - ratio );
					te = mix<float>( sx , wx , 1 - ratio );
					py = mix<float>( py , iy , 1 - ratio );

					sy = wy;
					bs = ix;
					be = wx;
				}
				else
				{
					sy = mix<float>( sy , wy , 1 - ratio );
					bs = mix<float>( px , ix , 1 - ratio );
					be = mix<float>( sx , wx , 1 - ratio );
				}
			}

			float iwt = 1.f / (te - ts);
			float iwb = 1.f / (be - bs);
			float ih = 1.f / (sy - py);
			_GfxVertex frame_vert[] = {
				{ ts , py , 0 , 1.f , iwt , ih  , },
				{ te , py , 0 , 1.f , 1.f , ih  , },
				{ bs , sy , 0 , 1.f , iwb , 1.f , },
				{ be , sy , 0 , 1.f , 1.f , 1.f , },
			};

			UINT pass = 0;
			do 
			{
				m_technique->Begin( pass , rc );
				rc.GetDX9()->DrawPrimitiveUP( D3DPT_TRIANGLESTRIP , 2 , frame_vert , sizeof(_GfxVertex) );
				m_technique->End( pass , rc );
			}while(pass);
		}
		m_texture->SetByTexture( NULL );
	}

} // namespace xeres
